<template>
  <a-spin class="full-screen-spin" :spinning="isLoading" size="large">
    <div class="task-mark">
      <div class="header flex-between">
        <div class="left-info">
          <div class="logo fc">
            <img src="@/assets/img/header-logo.svg" />
          </div>
          <div class="mark-info">
            <span class="task-name" :title="taskInfo.name" @click="router.push(`/dmp/detail/obj/${taskId}`)">{{ taskInfo.name }}/</span>
            <span class="mark-title">标注</span>
            <span class="task-type">目标检测</span>
          </div>
        </div>
        <div class="right-progress">
          <a-progress :percent="Number(taskProgressData.progress)" :stroke-color="'rgba(24, 104, 233, 1)'" />
        </div>
      </div>
      <div class="contain">
        <!-- 图片列表 -->
        <img-select
          ref="imgSelectRef"
          :task-progress-data="taskProgressData"
          :img-list="imgList"
          :current-mark-img-index="currentMarkImgIndex"
          @clickImg="(index:number) => selectImg(index)"
          @toggleLeftImgColl="resizeCanvas"
          @scroll="onImgListScroll"
        />
        <!-- 标注区域 -->
        <div ref="middleMarkRef" class="middle-mark">
          <div class="utils-bar">
            <div class="change-btn">
              <a-button class="btn" :disabled="currentMarkImgIndex == 0" @click="selectImg(currentMarkImgIndex - 1)">
                <i class="iconfont icon-arrow-left-s-line"></i>
              </a-button>
              <a-button class="btn" :disabled="currentMarkImgIndex == imgList.length - 1" @click="selectImg(currentMarkImgIndex + 1)">
                <i class="iconfont icon-arrow-right-s-line"></i>
              </a-button>
            </div>
            <div class="mark-tools">
              <span
                v-for="item in toolsBtn"
                :key="item.value"
                :class="[currentMarkType === item.value && 'tools-btn--active', 'tools-btn']"
                :title="item.name"
                @click="changeMarkType(item.value)"
              >
                <template v-if="item.value === 'move'">
                  <i class="iconfont" :class="[currentMarkType === 'move' ? 'icon-back_Fist' : 'icon-back_hand']"></i>
                </template>
                <i v-else :class="['iconfont', item.iconClass]"></i>
              </span>
              <div class="midLine"></div>
              <span class="tools-btn" :title="'放大'" @click.stop="scaleImg('zoomIn')"><i class="iconfont icon-zoom-in-line"></i></span>
              <span class="tools-btn" :title="'缩小'" @click.stop="scaleImg('zoomOut')"><i class="iconfont icon-zoom-out-line"></i></span>
              <span class="tools-btn" :title="'重置大小'" @click.stop="resetImg"><i class="iconfont icon-unzoom-in-line"></i></span>

              <div class="midLine"></div>

              <span :class="['tools-btn', historyIndex < 1 && 'tools-btn--disabled']" @click="historyIndex >= 1 && canvasUndo()">
                <i class="iconfont icon-arrow-go-back-line" :title="'返回'"></i>
              </span>
              <span
                :class="['tools-btn', historyIndex == history.length - 1 && 'tools-btn--disabled']"
                @click="historyIndex < history.length - 1 && canvasRedo()"
              >
                <i class="iconfont icon-arrow-go-forward-line" :title="'前进'"></i>
              </span>
            </div>
            <a-space class="save-btn">
              <a-button class="btn icon-with-btn" @click="openTagOption">
                <i class="iconfont icon-settings-line"></i>
                <span>标签</span>
              </a-button>
              <a-button type="primary" class="btn icon-with-btn" @click="onClickSaveMark">
                <i class="iconfont icon-check-line"></i>
                <span>保存</span>
              </a-button>
            </a-space>
          </div>
          <div class="mark-do-result">
            <div ref="markAreaRef" class="mark-area">
              <canvas v-if="isRenderCanvas" id="canvas" ref="canvasRef" width="200" height="200" style="border: 1px solid #ccc"></canvas>
              <mark-item-attr
                :show-component="showMarkItemAttr"
                class="mark-item-attr-box"
                :tag-list="tagOptionRef?.tagList || []"
                :selected-obj="selectedObj"
                @changeSelectObjTag="(markTagInfo: TaskTag)=>saveMarkTagInfo(markTagInfo,false)"
              />

              <!-- 标签和属性弹窗 -->
              <div v-show="showSelectTagPopup" ref="popupSelectTagRef" class="popup-tag no-select" :style="popupSelectTagStyle">
                <template v-if="showSelectTagPopup">
                  <!-- 选择标签 -->
                  <div v-if="!isInputAttr" class="popup-select-tag">
                    <div class="title">
                      选择标签
                      <span class="close-btn" @click="deleteActiveObject()">
                        <i class="iconfont icon-close-line"></i>
                      </span>
                    </div>
                    <div class="search">
                      <a-input
                        v-model:value="markTagSearchValue"
                        v-trim
                        placeholder="请输入标签"
                        allow-clear
                        :bordered="false"
                        @change="tagOptionRef.getTaskTag(taskInfo.id, markTagSearchValue)"
                        @keydown.stop=""
                      >
                        <template #prefix>
                          <i class="iconfont icon-search-line"></i>
                        </template>
                      </a-input>
                    </div>
                    <div class="content my-scrollbar">
                      <div v-for="item in tagOptionRef.tagList" :key="item.id" class="tag-li">
                        <div class="tag-name" @click="setPopupSelectTag(item)">
                          <span
                            :title="item.name"
                            :style="{ backgroundColor: hexToRgba(item.color, 0.375).rgba, color: item.color }"
                            @mouseenter="changeTagBorder($event, item, 'enter')"
                            @mouseleave="changeTagBorder($event, item, 'leave')"
                            >{{ item.name }}</span
                          >
                        </div>
                      </div>
                    </div>
                  </div>
                  <!-- 输入属性 -->
                  <div v-else-if="selectPopupTag" class="popup-fill-attribute popup-select-tag">
                    <div class="title">
                      <span v-if="!tagOptionRef.fixedTagId" class="back-btn" @click="backOutAttr">
                        <i class="iconfont icon-arrow-left-line"></i>
                      </span>
                      <span class="title-text" :title="selectPopupTag.name">{{ selectPopupTag.name }}</span>
                      <span class="close-btn" @click=";(markTagSearchValue = ''), deleteActiveObject()">
                        <i class="iconfont icon-close-line"></i>
                      </span>
                    </div>
                    <div class="content-fill my-scrollbar">
                      <div v-for="item in selectPopupTag.attributeList" :key="item.id" class="attribute-li">
                        <div class="attribute-name" :title="item.name">{{ item.name }}</div>
                        <a-input
                          v-model:value="item.value"
                          v-trim
                          placeholder="请输入名称"
                          allow-clear
                          @pressEnter="saveMarkTagInfo(selectPopupTag)"
                        />
                      </div>
                    </div>
                    <div class="btn-box">
                      <a-button class="btn" @click="backOutAttr()">取消</a-button>
                      <a-button type="primary" class="btn" @click="saveMarkTagInfo(selectPopupTag)">确定</a-button>
                    </div>
                  </div>
                </template>
              </div>
            </div>
            <div class="mark-result">
              <div class="result-top">
                <span>标注结果</span>
                <span class="result-btn">
                  <i class="iconfont icon-delete-bin-6-line" @click="removeAllMark"></i>
                  <i :class="['iconfont', showMarkResult ? 'icon-eye-line' : 'icon-eye-close-line']" @click="changeMarkObjListShow()"></i>
                </span>
              </div>
              <div class="result-list my-scrollbar">
                <div
                  v-for="(item, index) in markObjList"
                  :key="index"
                  class="result-li"
                  :style="calcResultItemStyle(item, index)"
                  @mouseenter="onResultMouse(index, 'enter', item.visible || true)"
                  @mouseleave="onResultMouse(index, 'leave', item.visible || true)"
                  @click="setActiveMark(item)"
                >
                  <div class="result-li-left">
                    <i :class="['iconfont', filterArrayByValue(item.type as string, toolsBtn)?.iconClass]"></i>
                    <span class="tag-text" :title="item.markTagInfo.name">{{ item.markTagInfo.name }}</span>
                  </div>

                  <span v-show="resultHoverIndex == index" class="result-li-right" @click.stop="removeMark(item, index)">
                    <i class="iconfont icon-close-line"></i>
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </a-spin>
  <tag-option
    ref="tagOptionRef"
    :task-info="taskInfo"
    :mark-tag-search-value="markTagSearchValue"
    :on-after-delete-tag="onAfterDeleteTag"
    :on-edit-tag-canvas-handler="onEditTagCanvasHandler"
    @updateNowGraph="updateNowGraph"
  />
</template>

<script lang="ts" setup>
  import { useDraggable, useEventListener } from '@vueuse/core'
  import { message } from 'ant-design-vue'
  import { fabric } from 'fabric'
  import { cloneDeep, hexToRgba, isEqual, throttle, uuid } from 'g6-fn'
  import { computed, nextTick, onMounted, ref, watch } from 'vue'
  import { useRoute, useRouter } from 'vue-router'

  import deleteBtnImg from '@/assets/img/dmp/delete-btn.svg'
  import useTimeLoading from '@/hooks/use-time-loading'
  import { dmpTaskApi } from '@/services/api'
  import { filterArrayByValue } from '@/utils'

  import ImgSelect from '../components/ImgSelect.vue'
  import { toolsBtn } from '../options'
  import { Attribute, MarkInfo, MarkObj, TaskDataLi, TaskTag } from '../types'
  import {
    actionHandler,
    anchorWrapper,
    calcNewPosition,
    deepCloneFabricObject,
    drawMarkInCanvas,
    getMarkPosition,
    getMarkPositionInCanvas,
    markInImage,
    markPositionInCanvas,
    polygonPositionHandler,
    setMarkTagText,
    SubLine
  } from '../utils'
  import MarkItemAttr from './components/MarkItemAttr.vue'
  import TagOption from './components/TagOption.vue'

  const route = useRoute()
  const router = useRouter()

  const { isLoading, openLoading, closeLoading } = useTimeLoading({ delayTime: 0, timeoutTime: 60000000 })

  const taskId = route.params.taskId as string
  const taskInfo = ref({
    id: '',
    name: '',
    taskType: 1
  })
  const taskProgressData = ref({
    progress: '0',
    totalCount: 0,
    noMarkCount: 0,
    hasMarkCount: 0
  })

  //idle:空闲 ready:鼠标按下准备绘制 drawing:正在绘制 selectTag选择标签  inputAttr:输入属性
  type DrawStatus = 'idle' | 'ready' | 'drawing' | 'selectTag' | 'inputAttr'
  const drawingStatus = ref<DrawStatus>('idle') //当前画布的操作状态
  type MouseStatus = 'down' | 'move' | 'up'
  let mouseStatus: MouseStatus = 'up' //鼠标当前在画布上的状态

  // 标签变量
  const markTagSearchValue = ref('')

  // 标注变量
  const markAreaRef = ref()
  const middleMarkRef = ref()
  const canvasRef = ref()
  const isRenderCanvas = ref(true) //是否显示canvas，用于切换图片时对canvas强行销毁
  let canvas: fabric.Canvas
  let myImage: fabric.Image
  // 初始化状态数组和指针
  const history = ref<any[]>([])
  let historyIndex = ref<number>(-1)
  const currentMarkType = ref('rect') // 当前操作类型
  let preMarkType = 'rect' // 上一次操作类型
  const currentMarkImgIndex = ref(-1)
  const currentMarkItem = computed(() => imgList.value[currentMarkImgIndex.value])
  let imgScale = 1 // 图片缩放比例
  const nowGraphFillColor = ref('rgba(24, 104, 233, 0.375)') // 当前图形填充色
  const nowGraphStrokeColor = ref('rgba(24, 104, 233, 1)') // 当前图形边框色

  //由于页面多个地方需要使用，且其中的三个参数为固定参数，所以进行偏函数封装，方便调用
  const _markInImage = (obj: any) => markInImage(canvas, myImage, obj, imgScale)

  // 重新渲染canvas
  const rerenderCanvas = () => {
    // 如果已有canvas节点，直接删除掉
    let canvasElement = document.querySelector('.mark-area .canvas-container')
    canvasElement?.parentNode?.removeChild(canvasElement)

    return new Promise(rs => {
      isRenderCanvas.value = false
      nextTick(async () => {
        isRenderCanvas.value = true
        nextTick(() => rs(true))
      })
    })
  }

  // 获取canvas上的所有标注结果
  const getCanvasObj = () => {
    return canvas.getObjects().filter(item => {
      // @ts-ignore
      const { x1, x2, y1, y2, type } = item
      const isFilterType = ['image', 'text'].includes(type || '')
      const isSubLine = [x1, x2, y1, y2].includes(99999) //因为辅助线长度为99999  所以可以根据长度判断是否为辅助线
      return !isFilterType && !isSubLine
    })
  }

  /* 绘制图形后选择标签----------------------------------------------------------------------- */
  const popupSelectTagRef = ref() //选择标签的总体DOM
  // 拖拽弹窗
  const {
    x: popupSelectTagX,
    y: popupSelectTagY,
    style: popupSelectTagStyle
  } = useDraggable(popupSelectTagRef, {
    initialValue: { x: 200, y: 200 }
  })
  const showSelectTagPopup = computed(() => ['selectTag', 'inputAttr'].includes(drawingStatus.value)) // 是否显示选择标签
  const isInputAttr = computed(() => ['inputAttr'].includes(drawingStatus.value))
  // 选择标签后默认聚焦第一个输入框
  watch(
    () => isInputAttr.value,
    val => {
      if (!val) return
      nextTick(() => {
        const input = document.querySelector('.popup-fill-attribute .ant-input') as HTMLInputElement
        input?.focus()
      })
    }
  )
  const selectPopupTag = ref<TaskTag | null>(null) //已选择的标签

  const initPopupSelectTag = () => {
    //清空标签搜索输入框，并重置标签列表
    markTagSearchValue.value = ''
    tagOptionRef.value.getTaskTag(taskId, markTagSearchValue.value)
  }

  const setPopupSelectTag = (item: TaskTag) => {
    initPopupSelectTag()
    selectPopupTag.value = cloneDeep(item)
    const hasAttr = selectPopupTag.value!.attributeList?.length
    if (hasAttr) {
      drawingStatus.value = 'inputAttr'
      return
    }
    saveMarkTagInfo(selectPopupTag.value)
  }

  const selectMarkItemTag = (markItem: fabric.Object | null) => {
    if (markItem === null) {
      drawingStatus.value = 'idle'
      return
    }
    drawingStatus.value = 'selectTag'
    canvas.setActiveObject(markItem)

    if (!selectedObj.value) {
      return
    }

    // 矩形参数计算
    // selectedObj.value?.on('modified', setSelectedObjToPopupPosition)
    setSelectedObjToPopupPosition()
    // 通过是否有固定id判断是否为连续标注
    let findIndex = tagOptionRef.value.tagList.findIndex((item: TaskTag) => item.id === tagOptionRef.value.fixedTagId)
    const hasFixedTag = tagOptionRef.value.fixedTagId && findIndex !== -1
    if (hasFixedTag) {
      selectPopupTag.value = cloneDeep(tagOptionRef.value.tagList[findIndex])
      const hasAttr = selectPopupTag.value!.attributeList?.length
      if (hasAttr) {
        drawingStatus.value = 'inputAttr'
        return
      }

      saveMarkTagInfo(selectPopupTag.value)
      return
    }

    // 如果当前是选择多边形,且最近两个点相等,则不做任何操作

    // const point1 = selectedObj.value.points?.at(-1)
    // const point2 = selectedObj.value.points?.at(-2)
    // if (currentMarkType.value === 'polygon' && isEqual(point1, point2)) {
    // }

    // showSelectTagPopup.value = true
  }

  const backOutAttr = () => {
    selectPopupTag.value = null
    drawingStatus.value = 'selectTag'
  }

  /* 标注结果显示逻辑----------------------------------------------------------------------- */
  const showMarkResult = ref(true) // 显示标注结果
  let hideResultIds = ref<string[]>([])
  // 删除指定id
  //eslint-disable-next-line
  const deleteHideId = (id: string) => {
    const i = hideResultIds.value.findIndex(it => it === id)
    i !== -1 && hideResultIds.value.splice(i, 1)
  }
  const isHideResult = (id: string) => hideResultIds.value.includes(id)
  const resultHoverIndex = ref()
  // 监听标注结果鼠标移入移出
  const onResultMouse = (index: number, type: string, isVisible: boolean) => {
    if (!isVisible) return
    resultHoverIndex.value = type === 'leave' ? null : index
  }
  const changeTagBorder = (e: any, item: TaskTag, type: string) => {
    e.target.style.border = type === 'leave' ? '1px solid rgba(0, 0, 0, 0.05)' : '3px solid ' + item.color
  }
  // 改变标注图形的显示隐藏
  const changeMarkObjListShow = async (val?: boolean) => {
    if (currentPolygon.value) {
      message.error('请先完成当前标注')
      return
    }
    if (showSelectTagPopup.value) {
      message.error('请先选择标签')
      return
    }
    showMarkResult.value = val ?? !showMarkResult.value //有传值则使用传值，否则取反

    await nextTick()
    // 取消选中所有已选择的对象
    canvas.discardActiveObject()

    markObjList.value?.forEach(item => {
      item.set({
        visible: showMarkResult.value
      })
      hideResultIds.value.push(item.uid)
    })

    if (showMarkResult.value) {
      hideResultIds.value = []
    }

    let textList = canvas.getObjects('text')
    textList.forEach(item => {
      item.set({
        visible: showMarkResult.value
      })
    })

    canvas.renderAll()
  }
  // 根据标注结果计算样式
  const calcResultItemStyle = (item: MarkObj, index: number) => {
    const itemColor = item.markTagInfo.color
    const isHover = resultHoverIndex.value === index // 是否hover
    const isSelect = index === selectedObjIndex.value // 是否选中

    const disabledStyle = {
      color: '#ceced4',
      background: '#fbfbfc',
      border: '1px solid rgba(0, 0, 0, 0.05)'
    }

    let style = {
      color: itemColor,
      background: hexToRgba(itemColor, isHover ? 0.3 : 0.375).rgba,
      border: isSelect ? `3px solid ${itemColor}` : isHover ? `1px solid ${itemColor}` : '1px solid rgba(0, 0, 0, 0.05)'
    }
    return isHideResult(item.uid) ? disabledStyle : style
  }

  // 加载标注图片
  const loadImage = (url: string) => {
    return new Promise(rs => {
      fabric.Image.fromURL(url, (img: any) => {
        img.hasControls = false // 禁止控制角
        imgScale = Math.min(canvas.getWidth() / img.width, canvas.getHeight() / img.height)
        img.scale(imgScale)
        myImage = img
        myImage.set({
          selectable: false,
          evented: false,
          type: 'image'
        })
        // 将图片添加到画布
        canvas.add(myImage)

        // 使图片居中
        canvas.centerObject(myImage)
        canvas.setBackgroundImage(myImage, canvas.renderAll.bind(canvas))
        // 刷新画布
        canvas.renderAll()
        subLine.init()
        fabric.util.requestAnimFrame(() => {
          console.log(`图片加载完成`)
          rs(true)
        })
      })
    })
  }

  let downPoint: fabric.Point | null // 鼠标按下的坐标

  let selectedObj = ref<MarkObj | null>() // 选中的对象
  let selectedObjIndex = computed(() => {
    if (!selectedObj.value) {
      return -1
    }
    return markObjList.value?.indexOf(selectedObj.value)
  }) // 选中的对象的索引
  let markObjList = ref<MarkObj[]>([]) // 标注数据

  // 拖动使用
  let isDragging = false
  let draggingObj = { lastPosX: 0, lastPosY: 0 }

  // 校验标注数据是否操作到一半
  const verifyMarkObjList = () => {
    if (showSelectTagPopup.value) {
      message.error({
        content: '请先选择标签',
        key: '请先选择标签'
      })
      return false
    }
    if (currentPolygon.value) {
      message.error({
        content: '请先完成当前标注',
        key: '请先完成当前标注'
      })
      return false
    }
    return true
  }

  // 写入标注数据
  const setSelectedObjToPopupPosition = () => {
    // 如果没有选中的对象，直接返回
    if (!selectedObj.value) {
      return
    }

    const canvasWidth = canvas.getWidth()
    const canvasHeight = canvas.getHeight()
    const pupTagHeight = 258
    const pupTagWidth = 160

    let { top = 0, left = 0, width = 0 } = selectedObj.value
    const zoom = canvas.getZoom()
    top = top * zoom
    left = left * zoom
    width = width * zoom
    let positionTop = top + canvas.viewportTransform![5]
    if (positionTop + pupTagHeight > canvasHeight) {
      positionTop = canvasHeight - pupTagHeight
    } else if (positionTop < 0) {
      positionTop = 0
    }
    let positionLeft = left + width + canvas.viewportTransform![4] + 10
    if (positionLeft + pupTagWidth > canvasWidth) {
      positionLeft = canvasWidth - pupTagWidth
    }

    popupSelectTagX.value = positionLeft + 155
    popupSelectTagY.value = positionTop + 44 + 56
  }

  const initCanvas = () => {
    canvas = new fabric.Canvas(canvasRef.value, {
      fireRightClick: true, // 启用右键，button的数字为3
      stopContextMenu: true // 禁止默认右键菜单
    }) // 创建画布
    canvas.selection = false // 禁止选中
    canvas.setWidth(middleMarkRef.value?.offsetWidth - 180)
    canvas.setHeight(markAreaRef.value.offsetHeight)
    subLine = new SubLine(canvas)

    canvas.on('mouse:down', e => {
      console.log('mouse:down')
      mouseStatus = 'down'
      changeCursor(e)

      downPoint = e.absolutePointer as fabric.Point

      // 移动画布
      if (currentMarkType.value === 'move') {
        // 鼠标按下时触发
        let evt = e.e
        isDragging = true // isDragging 是自定义的
        draggingObj.lastPosX = evt.clientX // lastPosX 是自定义的
        draggingObj.lastPosY = evt.clientY // lastPosY 是自定义的
        return
      }

      switch (drawingStatus.value) {
        case 'idle':
          // 右键点击
          if (e.button === 3 && e.target) {
            onMouseRightClick(e.target)
          }
          if (selectedObj.value) {
            return
          }
          // 如果是点，直接绘制，并绘制开始选择标签
          if (currentMarkType.value === 'point') {
            const obj = markPoint.create()
            selectMarkItemTag(obj)
            return
          }
          // 如果是多边形，则开始绘制点
          if (currentMarkType.value === 'polygon') {
            currentPolygon.value === null ? markPolygon.create(downPoint) : markPolygon.addPoint(downPoint)
            drawingStatus.value = 'drawing'
            return
          }
          drawingStatus.value = 'ready'
          return
        // 正在绘画，除多边形外，不作任何操作
        case 'drawing':
          // 鼠标双击
          if (e.e.detail === 2) {
            if (currentMarkType.value === 'polygon') {
              console.log('双击多边形')
              const obj = markPolygon.save(downPoint)
              drawingStatus.value = 'selectTag'
              selectMarkItemTag(obj)
              return
            }
          } else {
            markPolygon.addPoint(downPoint)
          }
          return
        //选择标签属性
        case 'selectTag':
        case 'inputAttr':
          // 判断是否点击的当前正在选择标签的元素
          const isSelectSelectedObj = isEqual(e.target, selectedObj.value)
          console.log('是否点击自身', isSelectSelectedObj, e.target)

          if (e.target && isSelectSelectedObj) {
            return
          }

          if (!verifyMarkObjList()) {
            // 如果没选择标签的对象则对该对象进行选中
            let graphArr = getCanvasObj()
            let noInfoObj = graphArr.filter((item: fabric.Object) => !Object.hasOwn(item, 'markTagInfo'))
            if (noInfoObj.length > 0) {
              if (Object.hasOwn(noInfoObj[0], 'type')) {
                canvas.setActiveObject(noInfoObj[0])
              }
            }
          }
          return
        default:
          break
      }
    })
    canvas.on('mouse:move', e => {
      mouseStatus = 'move'
      changeCursor(e)

      if (!e.absolutePointer) {
        return
      }

      // 更新辅助线位置
      subLine.setVisible(true)
      subLine.updatePosition(e.absolutePointer.x, e.absolutePointer.y)

      if (currentMarkType.value === 'move') {
        if (!isDragging) {
          return
        }
        // 处理拖拽
        canvas.setCursor('grabbing')
        const { x, y } = e.e
        const dx = x - draggingObj.lastPosX
        const dy = y - draggingObj.lastPosY
        draggingObj.lastPosX += dx
        draggingObj.lastPosY += dy
        // 移动画布
        canvas.relativePan({ x: dx, y: dy })
        // 移动所有图形
        // draggingObj.lastPosX = x
        // draggingObj.lastPosY = y
        return
      }

      // 根据绘画状态来判断当前是新增还是修改大小
      if (drawingStatus.value === 'ready') {
        switch (currentMarkType.value) {
          case 'rect':
            markRect.create()
            break
          case 'circle':
            markCircle.create()
            break
          case 'line':
          case 'dottedLine':
            markLineOrDottedLine.create()
            break
          default:
            break
        }
        drawingStatus.value = 'drawing'
        return
      }
      if (drawingStatus.value === 'drawing') {
        switch (currentMarkType.value) {
          case 'rect':
            markRect.resize(e.absolutePointer)
            break
          case 'circle':
            markCircle.resize(e.absolutePointer)
            break
          case 'polygon':
            markPolygon.resize(e.absolutePointer)
            break
          case 'line':
          case 'dottedLine':
            markLineOrDottedLine.resize(e.absolutePointer)
            break
          default:
            break
        }
      }
    })
    canvas.on('mouse:up', e => {
      console.log('mouse:up')

      isDragging = false //关闭拖动功能

      // 画点则不做任何操作
      if (currentMarkType.value === 'point') {
        return
      }

      const isMouseMove = mouseStatus === 'move' //鼠标状态为move说明鼠标在移动
      mouseStatus = 'up'

      changeCursor(e)

      if (isMouseMove === false) {
        // 如果当前状态为准备状态，则直接取消掉部分图形的绘画
        if (drawingStatus.value === 'ready' && ['rect', 'circle', 'line', 'dottedLine'].includes(currentMarkType.value)) {
          drawingStatus.value = 'idle'
        }
      }

      const absolutePointer = e.absolutePointer

      if (!downPoint || !absolutePointer) {
        return
      }

      if (drawingStatus.value === 'drawing') {
        let markItem: fabric.Object | null = null

        //根据当前图形类型，创建图形
        switch (currentMarkType.value) {
          case 'rect':
            markItem = markRect.save(absolutePointer)
            break
          case 'circle':
            markItem = markCircle.save(absolutePointer)
            break
          case 'line':
          case 'dottedLine':
            markItem = markLineOrDottedLine.save(absolutePointer)
            break
          default:
            break
        }

        // 除绘制多边形外，其他图形开始选择标签
        if (currentMarkType.value !== 'polygon') {
          selectMarkItemTag(markItem)
        }
      }

      downPoint = null
    })
    // 监听鼠标移入事件
    canvas.on('mouse:over', () => {})

    // 监听鼠标移出事件
    canvas.on('mouse:out', () => {
      subLine.setVisible(false)
    })
    // 监听鼠标滚动事件
    canvas.on('mouse:wheel', e => {
      console.log('mouse:wheel')
      let delta = e.e.deltaY // 滚轮，向上滚一下是 -100，向下滚一下是 100
      let zoom = canvas.getZoom() // 获取画布当前缩放值
      zoom *= 0.999 ** delta
      if (zoom > 20) {
        zoom = 20
      }
      if (zoom < 0.01) {
        zoom = 0.01
      }
      canvas.zoomToPoint(
        {
          // 关键点
          x: e.e.offsetX,
          y: e.e.offsetY
        },
        zoom
      )
      e.e.preventDefault()
      e.e.stopPropagation()
      canvas.requestRenderAll()
    })

    canvas.on('object:removed', () => {
      console.log('object:removed')
    })
    canvas.on('object:modified', e => {
      console.log('object:modified')

      let obj = e.target as MarkObj
      canvas.renderAll()

      /* 根据不同类型的图形，计算图形的位置 */
      if (obj instanceof fabric.Rect) {
        const newWidth = obj.width! * obj.scaleX!
        const newHeight = obj.height! * obj.scaleY!
        obj.set({ width: newWidth, height: newHeight, scaleX: 1, scaleY: 1 })
      } else if (obj instanceof fabric.Circle) {
        const newRadius = obj.get('radius')! * obj.scaleX!
        // 更新圆的尺寸
        obj.setRadius(newRadius)
        obj.set({ radius: newRadius, scaleX: 1, scaleY: 1 })
      } else if (obj instanceof fabric.Line) {
        const x1Tox2 = Math.abs(obj.x2! - obj.x1!) * obj.scaleX!
        const y1Toy2 = Math.abs(obj.y2! - obj.y1!) * obj.scaleY!

        const line = {
          x1: 0,
          y1: 0,
          x2: 0,
          y2: 0,
          scaleX: 1,
          scaleY: 1,
          flipX: false,
          flipY: false,
          direction: obj.direction
        }

        if (line.direction === 'leftToRightTopToBottom') {
          line.x1 = obj.left!
          line.y1 = obj.top!
          line.x2 = obj.left! + x1Tox2
          line.y2 = obj.top! + y1Toy2
        } else if (line.direction === 'rightToLeftTopToBottom') {
          line.x1 = obj.left! + x1Tox2
          line.y1 = obj.top!
          line.x2 = obj.left!
          line.y2 = obj.top! + y1Toy2
        } else if (line.direction === 'leftToRightBottomToTop') {
          line.x1 = obj.left!
          line.y1 = obj.top! + y1Toy2
          line.x2 = obj.left! + x1Tox2
          line.y2 = obj.top!
        } else if (line.direction === 'rightToLeftBottomToTop') {
          line.x1 = obj.left! + x1Tox2
          line.y1 = obj.top! + y1Toy2
          line.x2 = obj.left!
          line.y2 = obj.top!
        }
        obj.set(line)
      }

      let { isValidity, markObj } = _markInImage(obj)

      /* 校验未通过 */
      if (!isValidity) {
        canvas.remove(obj)
        const findIndex = markObjList.value.findIndex(item => isEqual(item, obj))
        if (findIndex !== -1) {
          markObjList.value?.splice(findIndex, 1)
        }
        selectPopupTag.value = null
        saveState()
        drawingStatus.value = 'idle'
        canvas.renderAll()
        return
      }

      // @ts-ignore
      const { markTagInfo = null } = obj

      if (markObj.type === 'polygon') {
        canvas.remove(obj)
        let deleteIndex = markObjList.value?.indexOf(obj)
        if (deleteIndex > -1) {
          markObjList.value![deleteIndex] = markObj // 删除一个元素
        }

        addPolygonControl(markObj)
        canvas.add(markObj)

        markObj.set({
          objectCaching: false,
          transparentCorners: false,
          hasBorders: false
        })
        if (obj.markTagInfo) {
          markObj.set({
            markTagInfo: obj.markTagInfo,
            fill: hexToRgba(obj.markTagInfo.color, 0.375).rgba,
            stroke: obj.markTagInfo.color
          })
          // 添加标注信息到画布
          setMarkTagText(canvas, markObj, obj.markTagInfo.name)
        }
      }
      setControlsStyle(markObj)

      if (markTagInfo) {
        markObj.set({
          markTagInfo: markTagInfo,
          fill: obj.type === 'point' ? markTagInfo.color : hexToRgba(markTagInfo.color, 0.375).rgba,
          stroke: markTagInfo.color
        })
      }

      canvas.setActiveObject(markObj!)
      saveState()

      canvas.renderAll()
    })
    // 元素改变大小
    canvas.on('object:scaling', () => {})
    canvas.on('object:moving', () => {})
    //选中图形
    canvas.on('selection:created', e => {
      onlySelectObj(e.selected![0])
      updateDeleteBtnShow(e.selected![0])
    })
    //选中图形切换
    canvas.on('selection:updated', e => {
      onlySelectObj(e.selected![0])
      updateDeleteBtnShow(e.selected![0])
    })

    canvas.on('selection:cleared', () => {
      clearSelectAllObj()
    })
  }

  // 判断鼠标是否在选中元素内
  const judgeMouseInSelectObj = (pointer: fabric.Point) => {
    if (!selectedObj.value) {
      return false
    }
    return selectedObj.value.containsPoint(pointer)
    // 判断当前鼠标位置是否有对象
  }

  const onMouseRightClick = (targetItem: fabric.Object) => {
    canvas.setActiveObject(targetItem)
    canvas.renderAll()
    changeMarkType(targetItem.type ?? 'rect')
  }

  //选中当前图形，取消选中其他图形
  const onlySelectObj = (obj: fabric.Object) => {
    const objs = getCanvasObj()
    objs.forEach(o => {
      o.set({
        selectable: o === obj
      })
      if (o === obj) {
        selectedObj.value = o
      }
    })
  }

  // 设置删除按钮显示
  const updateDeleteBtnShow = (obj: fabric.Object) => {
    // 正常状态需要显示删除按钮
    const isShowDeleteBtn = drawingStatus.value === 'idle'
    fabric.Object.prototype.controls.deleteControl.setVisibility(isShowDeleteBtn)
    // 多并行需特殊处理
    if (obj.type === 'polygon') {
      obj.controls.deleteControl.setVisibility(isShowDeleteBtn)
    }
  }

  //取消选中所有Obj
  const clearSelectAllObj = () => {
    const objs = getCanvasObj()
    objs.forEach(o => {
      o.set({
        selectable: false
      })
    })
    selectedObj.value = null
  }

  /*  创建图形---------------------------------------------------------------- */
  // 矩形
  let currentRect: fabric.Object | null // 临时矩形，创建矩形的时候使用
  const markRect = {
    create: () => {
      currentRect = new fabric.Rect({
        top: downPoint!.y,
        left: downPoint!.x,
        fill: nowGraphFillColor.value,
        stroke: nowGraphStrokeColor.value,
        type: 'rect',
        selectable: false, // 设置可选属性为true
        lockUniScaling: true // 禁止等比缩放
      })
      // 隐藏四个操作按钮，先注释，后续需要再加
      // currentRect.setControlVisible('ml', false)
      // currentRect.setControlVisible('mr', false)
      // currentRect.setControlVisible('mt', false)
      // currentRect.setControlVisible('mb', false)
      canvas.add(currentRect)
    },
    resize: (currentPoint: { y: number; x: number }) => {
      if (currentRect) {
        let top = Math.min(downPoint!.y, currentPoint.y)
        let left = Math.min(downPoint!.x, currentPoint.x)
        let width = Math.abs(downPoint!.x - currentPoint.x)
        let height = Math.abs(downPoint!.y - currentPoint.y)
        currentRect.set({
          top,
          left,
          width,
          height
        })
        canvas.requestRenderAll()
      }
    },
    save: (pointer: { y: number; x: number }) => {
      if (isEqual(downPoint, pointer)) {
        canvas.remove(currentRect as fabric.Object)
        currentRect = null
        return null
      }
      let { isValidity, markObj } = _markInImage(currentRect)

      if (!isValidity) {
        canvas.remove(currentRect!)
        downPoint = null
        currentRect = null
        return null
      }
      if (currentRect) {
        canvas.remove(currentRect!)
        markObj.set({
          selectable: true
        })
        canvas.add(markObj)
        setControlsStyle(markObj)
        canvas.setActiveObject(markObj)
      }
      canvas.renderAll()
      downPoint = null
      currentRect = null
      return markObj
    }
  }
  // 圆形
  let currentCircle: fabric.Circle | null // 临时圆，创建圆的时候使用
  const markCircle = {
    create: () => {
      currentCircle = new fabric.Circle({
        top: downPoint!.y,
        left: downPoint!.x,
        radius: 0,
        fill: nowGraphFillColor.value,
        stroke: nowGraphStrokeColor.value,
        type: 'circle'
      })

      canvas.add(currentCircle)
    },
    resize: (currentPoint: { y: number; x: number }) => {
      if (!currentCircle) {
        return
      }
      // 原点
      const initX = downPoint!.x
      const initY = downPoint!.y

      let radius = Math.max(Math.abs(initX - currentPoint.x), Math.abs(initY - currentPoint.y)) / 2
      let top = currentPoint.y > initY ? initY : initY - radius * 2
      let left = currentPoint.x > initX ? initX : initX - radius * 2
      currentCircle.set({
        radius,
        top,
        left
      })
      canvas.requestRenderAll()
    },
    save: (pointer: { y: number; x: number }) => {
      if (JSON.stringify(downPoint) === JSON.stringify(pointer)) {
        canvas.remove(currentCircle as fabric.Object)
        currentCircle = null

        return null
      }
      let { isValidity, markObj } = _markInImage(currentCircle)

      if (!isValidity) {
        canvas.remove(currentCircle!)
        return null
      }
      if (currentCircle) {
        canvas.remove(currentCircle!)
        markObj.set({
          selectable: true
        })
        canvas.add(markObj)
        setControlsStyle(markObj)
        canvas.setActiveObject(markObj)
      }
      currentCircle = null
      return markObj
    }
  }
  // 点
  const markPoint = {
    create: (): fabric.Object | null => {
      const point = new fabric.Circle({
        left: downPoint!.x - 5,
        top: downPoint!.y - 5,
        strokeWidth: 0,
        radius: 5,
        selectable: false, // 允许选中
        hasControls: true, // 显示控制点
        hasBorders: false, // 不显示边框
        type: 'point'
      })

      // 判断图形是否在图片内
      let { isValidity, markObj } = _markInImage(point)
      if (!isValidity) {
        return null
      }
      markObj.set({
        selectable: false
        // evented: false
      })
      canvas.add(markObj)
      markObj.setControlsVisibility({
        mt: false,
        mb: false,
        ml: false,
        mr: false,
        bl: false,
        br: false,
        tl: false,
        tr: false
      })
      setControlsStyle(markObj)
      markObj.set('fill', nowGraphStrokeColor.value)
      drawingStatus.value = 'selectTag'
      canvas.setActiveObject(markObj)
      canvas.renderAll()
      return markObj
    }
  }
  //线
  let currentLine: fabric.Line | null = null
  const markLineOrDottedLine = {
    create: () => {
      currentLine = new fabric.Line(
        [
          downPoint!.x,
          downPoint!.y, // 起始点坐标
          downPoint!.x,
          downPoint!.y // 结束点坐标
        ],
        {
          type: currentMarkType.value,
          stroke: nowGraphStrokeColor.value, // 笔触颜色
          strokeDashArray: currentMarkType.value === 'dottedLine' ? [5, 5] : []
        }
      )
      currentLine.setControlsVisibility({
        mt: false,
        mb: false,
        ml: false,
        mr: false
      })
      canvas.add(currentLine)
      return currentLine
    },
    resize: (currentPoint: { y: number; x: number }) => {
      if (currentLine) {
        currentLine.set('x2', currentPoint.x)
        currentLine.set('y2', currentPoint.y)
        canvas.requestRenderAll()
      }
    },
    save: (pointer: { y: number; x: number }) => {
      let returnMarkObj = null
      if (isEqual(downPoint, pointer)) {
        canvas.remove(currentLine as fabric.Line)
        returnMarkObj = null
      } else {
        // 判断图形是否在图片内
        let { isValidity, markObj } = _markInImage(currentLine)
        if (!isValidity) {
          canvas.remove(currentLine as fabric.Line)
          return null
        }
        canvas.remove(currentLine as fabric.Line)
        canvas.add(markObj)
        setControlsStyle(markObj as fabric.Line)
        markObj.setControlsVisibility({
          mt: false,
          mb: false,
          ml: false,
          mr: false
        })
        // const { x1, x2, y1, y2 } = markObj
        // let direction = ''
        // if (x1 < x2 && y1 < y2) {
        //   direction = 'leftToRightTopToBottom'
        // } else if (x1 > x2 && y1 < y2) {
        //   direction = 'rightToLeftTopToBottom'
        // } else if (x1 < x2 && y1 > y2) {
        //   direction = 'leftToRightBottomToTop'
        // } else if (x1 > x2 && y1 > y2) {
        //   direction = 'rightToLeftBottomToTop'
        // }
        markObj!.set({
          selectable: true,
          lockScalingFlip: true
          // direction: direction
        })
        canvas.setActiveObject(markObj)
        returnMarkObj = markObj
      }
      currentLine = null
      return returnMarkObj
    }
  }
  // 创建多边形
  let currentPolygon = ref<fabric.Polygon | null>(null) // 临时多边形
  const markPolygon = {
    create: (currentPoint: { y: number; x: number }) => {
      currentPolygon.value = new fabric.Polygon(
        [
          { x: currentPoint.x, y: currentPoint.y },
          { x: currentPoint.x, y: currentPoint.y }
        ],
        {
          fill: nowGraphFillColor.value,
          stroke: nowGraphStrokeColor.value,
          objectCaching: false,
          hasControls: false,
          transparentCorners: false
        }
      )
      canvas.add(currentPolygon.value)

      canvas.bringToFront(currentPolygon.value)
    },
    addPoint: (currentPoint: { y: number; x: number }) => {
      let points = currentPolygon.value!.points as fabric.Point[]

      points.push({
        x: currentPoint.x,
        y: currentPoint.y
      } as fabric.Point)
      canvas.requestRenderAll()
    },
    resize: (currentPoint: { y: number; x: number }) => {
      if (!currentPolygon.value) {
        return
      }
      let points = currentPolygon.value.points as fabric.Point[]
      // @ts-ignore
      Object.assign(points.at(-1), {
        x: currentPoint.x,
        y: currentPoint.y
      })
      canvas.requestRenderAll()
    },
    save: (currentPoint: { y: number; x: number }) => {
      let points = currentPolygon.value!.points as fabric.Point[]
      points.at(-1)!.x = currentPoint.x
      points.at(-1)!.y = currentPoint.y
      points.splice(points.length - 1) //多边形双击会多一个点，删除
      canvas.remove(currentPolygon.value as fabric.Polygon)
      // 判断多边形是否只有一个点
      if (points.length <= 1) {
        currentPolygon.value = null
        return null
      }
      // 新建多边形
      let polygon: fabric.Polygon & { edit?: any; tlCoordXy?: [number, number] } = new fabric.Polygon(points)
      // 判断图形是否在图片内
      let { isValidity, markObj } = _markInImage(polygon)
      if (!isValidity) {
        canvas.remove(polygon)
        currentPolygon.value = null
        return null
      }
      polygon = markObj

      polygon.set({
        tlCoordXy: [polygon.left!, polygon.top!],
        fill: nowGraphFillColor.value,
        strokeWidth: 1,
        stroke: nowGraphStrokeColor.value,
        objectCaching: false,
        transparentCorners: false,
        cornerColor: nowGraphStrokeColor.value,
        hasBorders: false,
        selectable: true
        // evented: false
      })

      addPolygonControl(polygon)
      canvas.add(polygon)
      canvas.setActiveObject(polygon)
      canvas.renderAll()
      currentPolygon.value = null
      return polygon
    }
  }
  // 创建多边形控制点
  const addPolygonControl = (polygon: any) => {
    let lastControl = polygon.points!.length - 1
    polygon.cornerStyle = 'circle'
    polygon.cornerColor = nowGraphStrokeColor.value
    polygon.controls = polygon.points!.reduce(function (acc: any, point: any, index: number) {
      acc['p' + index] = new fabric.Control({
        positionHandler: polygonPositionHandler,
        actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
        actionName: 'modifyPolygon',
        pointIndex: index
      } as fabric.IObjectOptions & { pointIndex?: number })
      return acc
    }, {})
    polygon.controls.deleteControl = new fabric.Control({
      x: 0.5,
      y: -0.5,
      offsetY: -11,
      offsetX: 10,
      cursorStyle: 'pointer',
      mouseUpHandler: deleteObject,
      render: renderIcon(deleteImg),
      visible: true,
      cornerSize
    } as fabric.IObjectOptions & { cornerSize?: number })

    polygon.hasBorders = false
    polygon.updated = true

    setControlsStyle(polygon!)
    canvas.requestRenderAll()
    return polygon
  }

  /*  ---------------------------------------------------------------- */

  // 标注结果选中标注  激活在画布
  const setActiveMark = (item: any) => {
    // 禁用状态下，禁止选中标注
    if (!item.visible) return

    let activeItem = canvas.getObjects().find(mark => isEqual(mark, item))
    if (!activeItem) {
      return
    }
    canvas.setActiveObject(activeItem)
    canvas.renderAll()
  }
  // 改变鼠标样式
  const changeCursor = (e: any) => {
    if (currentMarkType.value === 'move') {
      canvas.setCursor('grab')
      return
    }

    canvas.setCursor('crosshair')

    let pointer = e.pointer

    let activeObj = canvas.getActiveObject()
    let graphArr = getCanvasObj()
    let activeObjArea = activeObj?.getBoundingRect()

    if (judgeMouseInSelectObj(pointer)) {
      canvas.setCursor('move')
      return
    }

    // 在选中图形内和删除按钮内  改变鼠标样式
    if (activeObjArea) {
      let left = activeObjArea!.left + activeObjArea!.width + 8
      let top = activeObjArea!.top - 22
      let width = 22,
        height = 22
      const rect = new fabric.Rect({
        top,
        left,
        width,
        height,
        fill: nowGraphFillColor.value,
        stroke: nowGraphStrokeColor.value,
        type: 'circle',
        selectable: false // 设置可选属性为true
      })
      if (rect.containsPoint(pointer) || activeObj?.containsPoint(pointer) || e.transform?.action) {
        canvas.defaultCursor = 'move'
        return
      }
    }
    // 在已绘制的图形内  改变鼠标样式
    for (let i = 0; i < graphArr.length; i++) {
      if (graphArr[i].containsPoint(pointer) && !activeObj?.containsPoint(pointer) && !e.transform?.action) {
        canvas.setCursor('cell')
        return
      }
    }

    // 默认鼠标样式
    canvas.setCursor('crosshair')
  }

  // 写入元素的控制点
  const setControlsStyle = (graphObj: fabric.Object) => {
    Object.assign(graphObj, {
      transparentCorners: false, // 角填充色不透明
      cornerColor: '#fff', // 角的颜色
      cornerStyle: 'circle', // 角的样式（圆形）
      cornerStrokeColor: nowGraphStrokeColor.value,
      cornerSize: 14
    })

    // 禁用圆的上下左右四个拖拽点
    graphObj.setControlVisible('mtr', false) // 顶部旋转
    // 原型取消四个点，禁止拖拽成椭圆
    if (graphObj.type === 'circle') {
      graphObj.setControlVisible('mt', false) // 顶部中间
      graphObj.setControlVisible('mr', false) // 右侧中间
      graphObj.setControlVisible('mb', false) // 底部中间
      graphObj.setControlVisible('ml', false) // 左侧中间
    }
  }

  // 保存当前画布状态
  function saveState() {
    // 绘画过程中禁止保存历史记录
    if (drawingStatus.value !== 'idle') {
      return
    }

    const currentState = cloneDeep(getCanvasObj())
    // 清除当前历史索引后的历史状态
    history.value.splice(historyIndex.value + 1, history.value.length - historyIndex.value - 1)
    history.value.push(currentState)
    historyIndex.value = history.value.length - 1
    console.log('记录状态')
  }

  const resetHistory = () => {
    history.value = []
    historyIndex.value = -1
  }

  // 上一步操作
  const canvasUndo = async () => {
    // 判断是否为第一步
    if (historyIndex.value <= 0) return
    if (!verifyMarkObjList()) {
      return
    }
    openLoading()
    canvas.discardActiveObject()
    canvas.getObjects().forEach(item => {
      if (item.type === 'image') return
      canvas.remove(item)
    })
    historyIndex.value--
    await renderHistoryCanvas(historyIndex.value)
    fabric.util.requestAnimFrame(() => {
      console.warn('标注渲染完成3333')
      closeLoading()
    })
  }

  // 下一步操作
  const canvasRedo = async () => {
    // 判断是否为最后一步
    if (historyIndex.value === history.value.length - 1) return
    if (!verifyMarkObjList()) return
    openLoading()
    canvas.discardActiveObject()
    canvas.getObjects().forEach(item => {
      if (item.type === 'image') return
      canvas.remove(item)
    })
    historyIndex.value++
    await renderHistoryCanvas(historyIndex.value)
    fabric.util.requestAnimFrame(() => {
      console.warn('标注渲染完成3333')
      closeLoading()
    })
  }

  // 渲染画布到某一记录
  const renderHistoryCanvas = async (index: number) => {
    markObjList.value = []
    canvas.clear()
    await loadImage(currentMarkItem.value.fileUrl)
    let markInfoList = canvasObjToMarkInfoList(history.value[index])

    // 过滤被删除掉标签的数据
    const tagIdList: string[] = tagOptionRef.value.tagList.map((tag: TaskTag) => tag.id)
    markInfoList = markInfoList.filter(item => tagIdList.includes(item.id))

    openLoading()
    await renderMarkInfo(markInfoList)
    closeLoading()
  }

  const scaleImg = (type: string) => {
    if (showSelectTagPopup.value) {
      message.error('请先选择标签')
      return
    }
    let delta
    if (type === 'zoomIn') {
      delta = -100 // 滚轮，向上滚一下是 -100，向下滚一下是 100
    } else {
      delta = 100
    }
    let zoom = canvas.getZoom() // 获取画布当前缩放值
    zoom *= 0.999 ** delta
    if (zoom > 20) {
      zoom = 20
    }
    if (zoom < 0.01) {
      zoom = 0.01
    }
    // 画一个中心点，以中心点开始缩放
    let canvasCenter: fabric.Point | null = new fabric.Point(canvas.width! / 2, canvas.height! / 2)
    canvas.zoomToPoint(canvasCenter, zoom)
    canvasCenter = null
    canvas.requestRenderAll()
  }

  const resetImg = async () => {
    if (showSelectTagPopup.value) {
      message.error('请先选择标签')
      return
    }
    openLoading()
    // 重置标注信息
    let markInfoList = canvasObjToMarkInfoList()
    resetCanvas()
    await loadImage(currentMarkItem.value.fileUrl)
    await renderMarkInfo(markInfoList)
    canvas.discardActiveObject()
    closeLoading()
  }

  /* 标注删除-------------------------------------------------------------------------------------------- */
  let deleteImg = document.createElement('img') //删除图片元素的DOM
  deleteImg.src = deleteBtnImg
  let cornerSize = 18 // 删除按钮大小

  // 添加删除按钮控件
  fabric.Object.prototype.controls.deleteControl = new fabric.Control({
    x: 0.5,
    y: -0.5,
    offsetY: -11,
    offsetX: 19,
    cursorStyle: 'pointer',
    mouseUpHandler: deleteObject,
    render: renderIcon(deleteImg),
    visible: true,
    cornerSize
  } as fabric.IObjectOptions & { cornerSize?: number })

  // 通过点击删除按钮删除对象
  function deleteObject(eventData: any, transform: { target: any }) {
    let target = transform.target
    let deleteIndex = markObjList.value.findIndex(obj => obj.uid === target.uid)
    if (deleteIndex > -1) {
      markObjList.value.splice(deleteIndex, 1) // 删除一个元素
    }
    let canvas = target.canvas
    canvas.remove(target)
    canvas.requestRenderAll()
    downPoint = null
    saveState()
    return false
  }
  // 渲染元素的icon按钮
  function renderIcon(icon: HTMLElement) {
    let size = cornerSize
    return function (ctx: any, left: number, top: number, styleOverride: any, fabricObject: any) {
      ctx.save()
      ctx.translate(left, top)
      ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle))
      ctx.drawImage(icon, -size / 2, -size / 2, size, size)
      ctx.restore()
    }
  }

  const removeMark = (item: any, index: number) => {
    canvas.getObjects().some((i: any) => {
      if (isEqual(i, item)) {
        canvas.remove(i)
        saveState()
        canvas.renderAll()
        return true
      }
    })

    markObjList.value.splice(index, 1)
    canvas.renderAll()
  }
  // 删除所有标注对象
  const removeAllMark = () => {
    if (!verifyMarkObjList()) {
      return
    }
    canvas.getObjects().forEach((i: any) => {
      if (i.type !== 'image') {
        canvas.remove(i)
      }
    })
    markObjList.value = []
    saveState()
    canvas.renderAll()
  }

  /* -------------------------------------------------------------------------------------------- */

  // 切换标注类型
  const changeMarkType = (type: string) => {
    if (!verifyMarkObjList()) {
      return
    }
    currentMarkType.value = type
    preMarkType = type
    // const isEdit = currentMarkType.value === 'editMark'
    canvas.getObjects().forEach(obj => {
      const isImageOrTextObject = ['image', 'text'].includes(obj.type || '')
      if (isImageOrTextObject) {
        return
      }

      obj.set({
        selectable: false
      })
    })
  }

  // 渲染标注信息
  const renderMarkInfo = (markInfoList: MarkInfo[]) => {
    return new Promise(rs => {
      markInfoList.forEach((item: any) => {
        const markTagInfo: { [x: string]: any } = {}
        markTagInfo.attributeList = item.childList?.map((i: any) => {
          return {
            id: i.taskTagAttributeId || i.id, //兼容后台返回的taskTagAttributeId
            name: i.name,
            value: i.value
          }
        })
        markTagInfo.id = item.id || item.taskTagId //后台返的是taskTagId，所以需要兼容
        markTagInfo.name = item.name
        markTagInfo.color = item.color
        let pointsOfImg = JSON.parse(item.points)
        const type = item.type // 标注类型
        let pointsOfCanvas = markPositionInCanvas(myImage, pointsOfImg, imgScale, type)
        let drawObject = drawMarkInCanvas(markTagInfo, type, pointsOfCanvas)
        if (drawObject === null) {
          return
        }

        drawObject.uid = item.uid
        // 同步最新的标注结果所对应的标签信息
        syncNewTagInfo(drawObject, markTagInfo.id)

        canvas.add(drawObject)

        // 添加标注信息到画布
        const text = setMarkTagText(canvas, drawObject, drawObject.markTagInfo.name)

        // 设置显隐
        if (isHideResult(drawObject.uid)) {
          drawObject.set({ visible: false })
          text.set({ visible: false })
        }

        if (type === 'polygon') {
          addPolygonControl(drawObject)
        } else {
          setControlsStyle(drawObject)
        }
        // console.log('drawObject', drawObject)

        markObjList.value.push(drawObject)
      })
      // 在canvas渲染好后关闭loading
      fabric.util.requestAnimFrame(() => {
        rs(true)
      })
    })
  }

  // 获取标注信息
  const getMarkInfo = async (id: string) => {
    router.replace({
      query: {
        id: id
      }
    })

    let { data } = await dmpTaskApi.taskDataMarkInfo(id)
    imgSelectRef.value?.selectIndex(currentMarkImgIndex.value)
    await loadImage(data.fileUrl)

    let findIndex = imgList.value.findIndex((item: any) => item.id === id)
    if (findIndex !== -1) {
      imgList.value[findIndex].markType = data.markType
    }
    if (data.markInfoList) {
      await renderMarkInfo(data.markInfoList)
    }
  }

  const resetCanvas = () => {
    markObjList.value = []
    canvas.setZoom(1)
    canvas.clear()
    canvas.setViewportTransform([1, 0, 0, 1, 0, 0])
  }

  // 将canvas上的图形转换成markInfoList
  const canvasObjToMarkInfoList = (canvasObjects?: any[]) => {
    canvasObjects ??= getCanvasObj()

    if (canvasObjects.length === 0) {
      return []
    }
    let markInfoList: any[] = []
    canvasObjects.forEach((item: any) => {
      // 基于canvas坐标
      let points = getMarkPositionInCanvas(item)
      if (!points) {
        return
      }

      // TODO:不知道为啥，存在bug，此时imgScale的长度可能为0，所以重新给缩放赋值
      imgScale = Math.min(canvas.getWidth() / (myImage?.width || 0), canvas.getHeight() / (myImage?.height || 0))
      // 获取基于图片的点位坐标
      let endPoints = getMarkPosition(myImage, points, imgScale, item.type)
      points = endPoints

      markInfoList.push({
        uid: item.uid ?? uuid(), //唯一id，用来做前端各种判断
        id: item.markTagInfo.id,
        name: item.markTagInfo.name,
        color: item.markTagInfo.color,
        type: item.type,
        childList: item.markTagInfo.attributeList,
        points: JSON.stringify(points)
      })
    })

    return markInfoList
  }

  const onClickSaveMark = async () => {
    openLoading({ tip: '数据保存中' })
    await saveMark(true)
    closeLoading()
  }

  // 保存标注
  const saveMark = async (isAutoNext = false) => {
    if (!verifyMarkObjList()) {
      return false
    }
    let canvasObjects = getCanvasObj()

    if (canvasObjects?.length !== markObjList.value?.length) {
      message.error('所有标注必须设置标签')
      return false
    }

    let markInfoList = canvasObjToMarkInfoList()

    // TODO:处理坐标[0,0]的特殊情况-----------------------------------------------------------
    let flag = false
    if (markInfoList.length > 0) {
      // 拿到第一个元素的所有点数据
      const firstItem = markInfoList[0]
      const points: [number, number][] = JSON.parse(firstItem.points)
      // 判断所有点位的数据是否全是[0,0]
      flag = points.every(point => point[0] === 0 && point[1] === 0)
    }
    if (flag) {
      message.error('保存失败，请刷新后重试')
      return
    }
    // TODO:-----------------------------------------------------------

    let params = {
      id: currentMarkItem.value.id,
      markInfoList,
      taskType: 2
    }

    await dmpTaskApi.taskDataMark(params, {
      isAddUid: true
    })

    showMarkResult.value = true
    getTaskProgress(taskId)
    resetHistory()
    // 模拟刷新标注状态
    currentMarkItem.value.markType = canvasObjects.length !== 0 ? 1 : 0

    // 是否需要自动加载下一张
    if (isAutoNext) {
      // 判断是否为最后一张
      const isNextNull = currentMarkImgIndex.value >= imgList.value.length - 1
      if (isNextNull) {
        message.success('保存成功，当前为最后一张图片')
      } else {
        currentMarkImgIndex.value = currentMarkImgIndex.value + 1
        resetCanvas()
        await getMarkInfo(currentMarkItem.value.id)
      }
    }

    return true
  }
  // 保存标注的标签内容
  const saveMarkTagInfo = (markTagInfo: TaskTag | null, isAdd = true) => {
    if (!markTagInfo || !selectedObj.value) {
      return
    }

    drawingStatus.value = 'idle'

    // 找到id为selectedObj.value.uid的text对象并删除
    if (selectedObj.value!.uid) {
      const selectObjText = canvas.getObjects().find((item: any) => item.markObjId === selectedObj.value!.uid)
      canvas.remove(selectObjText as fabric.IText)
    }

    // 将数据绑定到图形
    const color = markTagInfo.color
    selectedObj.value.set({
      markTagInfo: cloneDeep(markTagInfo),
      stroke: color,
      fill: selectedObj.value.type !== 'point' ? hexToRgba(color || '', 0.375).rgba : color,
      uid: uuid()
    })

    // TODO: 添加标注信息到画布
    setMarkTagText(canvas, selectedObj.value, markTagInfo.name)

    // 保存到标注数据中
    if (isAdd) {
      // 清除选择
      markObjList.value?.push(selectedObj.value)
      canvas.discardActiveObject()
      selectPopupTag.value = null
    }

    saveState()
    canvas.renderAll()
  }
  const deleteActiveObject = () => {
    initPopupSelectTag()
    tagOptionRef.value.getTaskTag(taskId, markTagSearchValue.value)

    const activeObject = canvas.getActiveObject()
    if (!activeObject) {
      return
    }
    canvas.remove(activeObject) // 从画布中删除对象
    const index = markObjList.value!.indexOf(activeObject)
    if (index > -1) {
      markObjList.value!.splice(index, 1) // 从数组中删除对象
      saveState()
    }
    drawingStatus.value = 'idle'
    canvas.renderAll()
  }

  /* 左侧图片渲染数据逻辑----------------------------------------------------------------------------------------------------------------------------------- */
  type ImgData = TaskDataLi | { id: string }
  const imgList = ref<ImgData[]>([]) // 任务图片列表
  let mockImgList: ImgData[] = [] // 模拟图片列表
  const imgSelectRef = ref<InstanceType<typeof ImgSelect>>()
  let isImgSelectInit = false //是否已经初始化图片选择组件

  //根据总数初始化虚拟图片列表
  const createMockImgList = (total: number) => {
    const list = []
    for (let i = 0; i < total; i++) {
      list[i] = { id: String(i) }
    }
    mockImgList = list
  }
  //判断元素是否为真实任务图片数据
  const judgeIsTaskDataLi = (item: ImgData) => typeof item.id === 'string'
  // 根据图片信息，获取图片当前是第多少张图片
  const getImgStartIndex = async (taskId: string, itemId: string): Promise<number> => {
    const { data: index } = await dmpTaskApi.getImgStartIndex({ id: itemId, taskId })
    return index
  }
  let prePartImgStartIndex: null | number = null //上次获取的部分图片列表的开始索引
  // 获取部分任务真实图片数据
  const getPartImgList = async (_startIndex?: number) => {
    let startIndex = _startIndex || imgSelectRef.value?.startIndex || 0
    const total = taskProgressData.value.totalCount
    // 如果开头数据不足以显示在列表第一个，则更新开始索引到第一个
    if (total - startIndex <= 20) {
      startIndex = total - 20 >= 0 ? total - 20 : 0
    }

    //为0则代表imgList还未加载数据
    if (total === 0) return

    // 防止index一致时重复请求
    if (startIndex === prePartImgStartIndex) return

    prePartImgStartIndex = startIndex

    let params = {
      taskId,
      start: startIndex,
      limit: 20
    }
    let { data } = await dmpTaskApi.partImgList(params)
    const list = data.records

    list.forEach((imgLi: TaskDataLi, i: number) => {
      mockImgList[startIndex + i] = imgLi
    })
    imgList.value = cloneDeep(mockImgList)

    await nextTick()

    isImgSelectInit = true //获取数据则说明图片首次加载完成
  }
  // 根据滚动，动态加载imgList数据
  let imgListScrollTimer: any = null //滚动防抖
  const onImgListScroll = () => {
    if (!isImgSelectInit || isLoading.value) return
    imgListScrollTimer && clearTimeout(imgListScrollTimer)
    imgListScrollTimer = setTimeout(() => {
      console.log('滚动逻辑触发，加载图片')
      getPartImgList()
    }, 200)
  }

  let isSelectImgFnFlag = false //选择图片节流阀，防止重复执行该方法
  const startSelectImg = () => {
    isSelectImgFnFlag = true
  }
  const endSelectImg = () => {
    isSelectImgFnFlag = false
    closeLoading()
  }
  // 选择任务图片
  const selectImg = async (index: number) => {
    if (isSelectImgFnFlag) {
      return
    }
    startSelectImg()

    // 重置标注信息时不允许切换，防止错位
    if (!verifyMarkObjList()) {
      endSelectImg()
      return
    }

    // 如果当前选择了图片，并且不在初始化渲染标注数据，则触发保存
    if (currentMarkImgIndex.value !== -1) {
      openLoading({ tip: '数据保存中' })
      const saveOk = await saveMark()
      if (!saveOk) {
        endSelectImg()
        return
      }
    }

    const isTaskImgLi = judgeIsTaskDataLi(imgList.value[index]) //判断是否为任务图片列表，如果是假数据，则重新获取数据
    if (!isTaskImgLi) {
      await getPartImgList(index)
    }

    currentMarkImgIndex.value = index
    const item = imgList.value[index]

    // 重置canvas及页面其他状态
    showMarkResult.value = true
    selectedObj.value = null
    markObjList.value = []
    openLoading()
    await rerenderCanvas()
    initCanvas()
    try {
      await getMarkInfo(item.id as string)
    } catch (error: any) {
      message.error(error.message || error)
      isSelectImgFnFlag = false
    }
    saveState()
    endSelectImg()
  }

  /* 任务数据逻辑----------------------------------------------------------------------------------------------------------------------------------- */
  // 获取任务进度
  const getTaskProgress = async (taskId: string) => {
    let { data } = await dmpTaskApi.taskProgress(taskId)
    if (data) {
      taskProgressData.value = data
    }
  }
  // 获取任务详情
  const getTaskDetail = async (taskId: string) => {
    let { data } = await dmpTaskApi.detail(taskId)
    taskInfo.value = data
  }

  /*  复制粘贴逻辑 -------------------------------------------------------------------------------------------------------------------------*/
  let copyItem: MarkObj | null = null

  // 更新赋值元素
  const updateCopyItem = () => {
    if (!selectedObj.value) {
      message.error('请选择元素')
      return
    }
    copyItem = cloneDeep(selectedObj.value)
    message.success('复制成功')
  }

  // 将复制元素写入画布
  const addCopyItem = () => {
    if (!copyItem) {
      message.error('请选择粘贴元素')
      return
    }
    // 克隆并计算元素位置
    let _copyItem = deepCloneFabricObject(copyItem)
    calcNewPosition(markObjList.value, _copyItem)

    let { isOutImg } = _markInImage(_copyItem)
    if (isOutImg) {
      message.error('粘贴元素超出图片范围')
      return
    }

    if (_copyItem.type === 'polygon') {
      addPolygonControl(_copyItem)
    } else {
      setControlsStyle(_copyItem)
    }
    canvas.add(_copyItem)
    canvas.renderAll()
    message.success('粘贴成功')
    // 判断是否需要选择标签
    if (copyItem.markTagInfo) {
      _copyItem.markTagInfo = cloneDeep(copyItem.markTagInfo)
      markObjList.value.push(_copyItem)
      if (_copyItem.type === 'polygon') {
        addPolygonControl(_copyItem)
      }
      setMarkTagText(canvas, _copyItem, _copyItem.markTagInfo.name)
      canvas.setActiveObject(_copyItem)
      saveState()
      return
    }
    selectMarkItemTag(_copyItem)
  }
  const throttleAddCopyItem = throttle(addCopyItem, 500, { trailing: false })
  /*  标签/属性修改逻辑逻辑 -------------------------------------------------------------------------------------------------------------------------*/
  const showMarkItemAttr = computed(() => !!(tagOptionRef.value?.tagList && selectedObj.value?.markTagInfo))
  /* 辅助线逻辑--------------------------------------------------------------- */
  let subLine: SubLine

  /* tag-options标签操作--------------------------------------------------------------- */
  const tagOptionRef = ref()
  const getTagDetail = async (taskId: string) => {
    await Promise.all([tagOptionRef.value.getTaskTag(taskId), tagOptionRef.value.getTaskTag(taskId)])
  }
  const openTagOption = () => {
    if (!verifyMarkObjList()) {
      return
    }

    // 取消canvas选中，防止修改标签后标签详情数据不同步
    canvas.discardActiveObject()
    canvas.renderAll()

    tagOptionRef.value.open()
  }
  const onAfterDeleteTag = async (tagId: string) => {
    // 找到标签对应元素并删除
    let shouldRemove = getCanvasObj().filter((mark: MarkObj) => mark.markTagInfo?.id === tagId)
    markObjList.value = markObjList.value.filter((mark: MarkObj) => mark.markTagInfo?.id !== tagId)

    shouldRemove!.forEach(mark => canvas.remove(mark))
    if (shouldRemove.length) {
      saveState()
    }
    if (copyItem?.markTagInfo?.id === tagId) {
      copyItem.markTagInfo = null
      copyItem.set({
        fill: nowGraphFillColor.value,
        stroke: nowGraphStrokeColor.value
      })
    }
    canvas.renderAll()
    await tagOptionRef.value.getTaskTag(taskId)
    await getTaskProgress(taskId)

    createMockImgList(taskProgressData.value.totalCount)
    await getPartImgList()
  }

  /**
   * @function 同步标注元素的最新标签信息
   * 对于已经标注的图形，修改标签后，需要做的操作：
   * 1. 复制一份标注信息
   * 2. 删除标注信息
   * 3. 遍历新的标签的属性，找到复制的标注信息中对应的属性，将属性值赋值给新的标签的属性
   */
  const syncNewTagInfo = (markObj: MarkObj, tagId: string) => {
    const tag: TaskTag = tagOptionRef.value.tagList.find((item: TaskTag) => item.id === tagId)
    markObj.markTagInfo.attributeList ??= []
    const copyObjAttributeList = cloneDeep(markObj.markTagInfo.attributeList) as Attribute[]

    markObj.markTagInfo.attributeList.splice(0)

    tag.attributeList?.forEach(a1 => {
      const findItem = copyObjAttributeList.find(a2 => a1.id === a2.id)
      markObj.markTagInfo.attributeList.push({
        ...a1,
        value: findItem?.value ?? ''
      })
    })

    Object.assign(markObj.markTagInfo, {
      color: tag.color,
      name: tag.name
    })

    markObj.set({
      fill: hexToRgba(tag.color!, 0.375).rgba,
      stroke: tag.color
    })
  }

  // 新增或编辑标签之后操作canvas的回调
  const onEditTagCanvasHandler = (tag: TaskTag) => {
    getCanvasObj()
      .filter((item: any) => item.markTagInfo?.id === tag.id)
      .forEach((markObj: MarkObj) => {
        syncNewTagInfo(markObj, tag.id)

        // 找到之前的text，删除并新增一个新的text
        const text = canvas.getObjects('text').find((item: MarkObj) => item.markObjId === markObj.uid)
        text && canvas.remove(text)
        setMarkTagText(canvas, markObj, tag.name)
        canvas.renderAll()
      })

    // 如果有已经复制的元素，则修改复制元素的标签
    if (copyItem?.markTagInfo?.id === tag.id) {
      copyItem!.markTagInfo = null
      copyItem?.set({
        fill: hexToRgba(tag.color!, 0.375).rgba,
        stroke: tag.color
      })
    }
    markObjList.value = getCanvasObj()
  }

  const updateNowGraph = (obj: { nowGraphFillColor: string; nowGraphStrokeColor: string }) => {
    nowGraphFillColor.value = obj.nowGraphFillColor // 当前图形填充色
    nowGraphStrokeColor.value = obj.nowGraphStrokeColor // 当前图形边框色
  }
  /*  其他逻辑 -------------------------------------------------------------------------------------------------------------------------*/
  // 切换选中标注到下一个
  const changeSelectObjToNext = () => {
    const selectId = selectedObj.value?.uid
    const _markObjList = getCanvasObj().filter(obj => obj.visible) //获取所有已显示的元素
    if (_markObjList.length === 0) {
      message.error('暂无可选择标注')
      return
    }
    const selectedObjIndex = _markObjList.findIndex(item => item.uid === selectId)
    // 选中的是最后一个或者没有选中
    const isGoFirstObj = selectId === undefined || selectedObjIndex === -1 || selectedObjIndex === _markObjList.length - 1
    canvas.setActiveObject(_markObjList[isGoFirstObj ? 0 : selectedObjIndex + 1])
    canvas.renderAll()
  }
  // 鼠标快捷键
  const handleKeyDown = (event: KeyboardEvent) => {
    // 禁用Ctrl + S默认事件 ,因为和浏览器热键有冲突
    if (event.ctrlKey && event.code === 'KeyS') {
      event.preventDefault()
    }

    if (isLoading.value) {
      return
    }

    if (drawingStatus.value !== 'idle') {
      return
    }

    if (tagOptionRef.value.tagVisible) {
      return
    }

    event.preventDefault()
    // Ctrl + Shift + Z
    if (event.shiftKey && event.code === 'KeyZ' && !event.altKey && event.ctrlKey) {
      canvasRedo()
      return
    }
    // Ctrl +
    if (event.ctrlKey && !event.altKey && !event.shiftKey) {
      switch (event.code) {
        // Ctrl + S
        case 'KeyS':
          onClickSaveMark()
          return
        // Ctrl + Z
        case 'KeyZ':
          canvasUndo()
          return
        // Ctrl + C
        case 'KeyC':
          updateCopyItem()
          return
        // Ctrl + V
        case 'KeyV':
          throttleAddCopyItem()
          return
        default:
          return
      }
    }
    // Shift +
    if (event.shiftKey && !event.altKey && !event.ctrlKey) {
      switch (event.code) {
        // Shift + Delete
        case 'Delete':
          removeAllMark()
          return
        // Shift + H
        case 'KeyH':
          changeMarkObjListShow()
          return
        default:
          return
      }
    }
    // 单键
    if (!event.ctrlKey && !event.altKey && !event.shiftKey) {
      switch (event.code) {
        // Delete
        case 'Delete':
          deleteActiveObject()
          return
        // Escape
        case 'Escape':
          resetImg()
          return
        // A
        case 'KeyA':
          if (currentMarkImgIndex.value === 0) {
            return false
          }
          selectImg(currentMarkImgIndex.value - 1)
          return
        // D
        case 'KeyD':
          if (currentMarkImgIndex.value === imgList.value.length - 1) {
            return false
          }
          selectImg(currentMarkImgIndex.value + 1)
          return
        // Q
        // 增添快捷键 Q 键，功能为按照标注结果的标注顺序从上至下依次切换选中标注框，支持可循环切换选中（就是已经在处于选中最后一个标注框时，再按 Q 键就切换选中当前的第一个标注框）
        case 'KeyQ':
          changeSelectObjToNext()
          return
        // Space
        case 'Space':
          if (currentMarkType.value !== 'move') {
            preMarkType = currentMarkType.value
            currentMarkType.value = 'move'
          }

          return
        default:
          return
      }
    }
  }
  const handleKeyUp = (event: KeyboardEvent) => {
    event.preventDefault()
    if (event.code === 'Space') {
      currentMarkType.value = preMarkType
    }
  }
  // 更改canvas画图宽高
  const resizeCanvas = async () => {
    await nextTick()
    if (!canvas) {
      return
    }
    canvas.setWidth(middleMarkRef.value?.offsetWidth - 180)
    canvas.setHeight(markAreaRef.value.offsetHeight)
    canvas.renderAll()
  }
  useEventListener(window, 'keydown', handleKeyDown)
  useEventListener(window, 'keyup', handleKeyUp)
  useEventListener(window, 'resize', resizeCanvas)

  onMounted(async () => {
    const itemId = route.query.id as string // 图片id

    openLoading({ tip: '页面加载中' })

    await getTaskProgress(taskId)
    getTaskDetail(taskId)
    await getTagDetail(taskId)
    createMockImgList(taskProgressData.value.totalCount)
    const startIndex = await getImgStartIndex(taskId, itemId)
    await getPartImgList(startIndex - 10 >= 0 ? startIndex - 10 : 0) //防止图片列表中间，显示不全

    selectImg(startIndex)
  })
</script>

<style lang="less" scoped>
  @import '@/styles/mixins.less';

  :deep(.ant-checkbox-group) {
    width: 100%;

    .ant-checkbox-wrapper {
      width: 100%;
    }

    .ant-checkbox + span {
      width: 90%;
    }
  }

  .flex-between {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .task-mark {
    width: 100vw;
    min-width: @min-width;
    height: 100vh;
    background: #fff;

    .header {
      padding-right: 24px;
      height: 44px;
      border-bottom: 1px solid #d9d9db;

      .left-info {
        display: flex;
        align-items: center;
        height: 100%;

        .logo {
          display: flex;
          align-items: center;
          padding: 0 25px;
          height: 100%;
          border-right: 1px solid #d9d9db;

          img {
            width: 22px;
            height: 22px;
            object-fit: cover;
          }
        }

        .mark-info {
          display: flex;
          align-items: center;

          .task-name {
            margin-left: 24px;
            max-width: 600px;
            color: rgba(25, 25, 48, 0.45);
            cursor: pointer;
            .elliptical-1();
          }

          .mark-title {
            margin-left: 8px;
            color: rgba(16, 16, 20, 0.95);
            .font16b();
          }

          .task-type {
            display: inline-block;
            padding: 3px 6px;
            margin-left: 16px;
            color: #4154d1;
            background: rgba(65, 84, 209, 0.08);
            border: 1px solid rgba(0, 0, 0, 0.05);
            border-radius: 6px;
            line-height: 22px;
          }
        }
      }

      .right-progress {
        width: 133px;
      }
    }

    .contain {
      display: flex;
      align-items: center;
      width: 100%;
      max-width: 100%;
      height: calc(100% - 44px);
      // overflow: hidden;

      .middle-mark {
        position: relative;
        overflow: hidden;
        padding-top: 56px;
        height: 100%;
        flex: 1;

        .utils-bar {
          position: absolute;
          top: 0;
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 4px 8px;
          width: 100%;
          height: 56px;
          color: rgba(16, 16, 20, 0.95);
          background: #fff;
          box-shadow: 0 1px 3px rgba(37, 37, 45, 0.16);

          .change-btn {
            display: flex;
            padding-right: 90px;

            .btn {
              display: flex;
              justify-content: center;
              align-items: center;
              width: 32px;
              height: 40px;
              text-align: center;
              border-radius: 6px;

              &:last-child {
                margin-left: 4px;
              }
            }
          }

          .mark-tools {
            display: flex;
            justify-content: center;
            align-items: center;
            // padding-right: 19px;
            // padding-left: 10px;
            width: 100%;
            height: 20px;
            font-size: 18px;

            .tools-btn {
              // padding: 7px;
              margin-right: 22px;
              width: 26px;
              height: 26px;
              text-align: center;
              line-height: 26px;
              cursor: pointer;

              &:hover,
              &--active {
                background: rgba(232, 232, 235, 1);
                border-radius: 6px;
              }

              &--disabled {
                border-radius: 6px;

                &:hover {
                  background: #fff;
                }

                .iconfont.icon-arrow-go-back-line {
                  font-weight: 500;
                  font-size: 18px;
                  color: #ccc;
                }

                .iconfont.icon-arrow-go-forward-line {
                  font-weight: 500;
                  font-size: 18px;
                  color: #ccc;
                }
              }

              i {
                font-weight: 500;
                font-size: 18px;
                color: #000;
              }

              &:last-child {
                margin-right: 0;
              }
            }

            .midLine {
              margin-right: 22px;
              width: 1px;
              height: 20px;
              background: #d9d9db;
              box-sizing: border-box;
            }
          }

          .save-btn {
            display: flex;
            justify-content: flex-end;
            flex-shrink: 0;

            .btn {
              display: flex;
              justify-content: center;
              align-items: center;
              width: 82px;

              span {
                &:first-child {
                  margin-right: 8px;
                }
              }
            }
          }
        }

        .mark-do-result {
          position: relative;
          display: flex;
          justify-content: space-between;
          height: 100%;
          background: #fafafa;

          .mark-area {
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            background: #dbdbdb;
            flex: 1;

            .mark-item-attr-box {
              position: absolute;
              top: 0;
              right: 0;
              z-index: 99;
            }

            .popup-tag {
              position: fixed;
              z-index: 99999;

              .popup-select-tag {
                overflow: hidden;
                width: 160px;
                border: 1px solid @border1;
                border-radius: 6px;

                .title {
                  position: relative;
                  display: flex;
                  align-items: center;
                  padding: 0 4px;
                  height: 28px;
                  background: rgba(245, 245, 245, 1);
                  border-radius: 6px 6px 0 0;
                  line-height: 28px;

                  .close-btn {
                    position: absolute;
                    right: 8px;
                    font-size: 14px;
                    cursor: pointer;
                  }

                  .title-text {
                    display: inline-block;
                    width: calc(100% - 50px);
                    .elliptical-1();
                  }
                }

                .search {
                  height: 32px;
                  border-bottom: 1px solid #e8e8eb;
                  background-color: #fff;

                  :deep(.ant-input-affix-wrapper) {
                    min-height: 32px !important;
                  }
                }

                .content {
                  overflow: auto;
                  padding: 6px 12px 0;
                  width: 100%;
                  height: 196px;
                  background: #fff;

                  .tag-name {
                    width: 100%;

                    span {
                      display: inline-block;
                      display: flex;
                      align-items: center;
                      padding: 0 6px;
                      margin-bottom: 12px;
                      width: 100%;
                      height: 30px;
                      text-align: left;
                      color: #1868e9;
                      background: rgba(24, 104, 233, 0.08);
                      border: 1px solid rgba(0, 0, 0, 0.05);
                      border-radius: 6px;
                      cursor: pointer;
                      .elliptical-1();
                    }
                  }
                }
              }

              .popup-fill-attribute {
                overflow: hidden;
                border: 1px solid @border1;
                border-radius: 6px;

                .title {
                  .back-btn {
                    margin-right: 4px;
                    width: 22px;
                    height: 22px;
                    text-align: center;
                    border-radius: 6px;
                    line-height: 22px;
                    cursor: pointer;

                    &:hover {
                      background: rgba(0, 0, 0, 0.05);
                    }
                  }
                }

                .content-fill {
                  overflow: auto;
                  padding: 6px 12px;
                  width: 100%;
                  height: 172px;
                  background: #fff;

                  .attribute-li {
                    margin-bottom: 12px;

                    .attribute-name {
                      overflow: hidden;
                      margin-bottom: 3px;
                      width: 130px;
                      font-size: 12px;
                      text-overflow: ellipsis;
                      white-space: nowrap;
                      line-height: 20px;
                    }

                    :deep(.ant-input-affix-wrapper) {
                      height: 32px;
                      min-height: 32px !important;
                    }
                  }
                }

                .btn-box {
                  display: flex;
                  justify-content: space-between;
                  padding: 12px;
                  width: 100%;
                  height: 58px;
                  background: #fff;
                  border-top: 1px solid #e8e8eb;
                }
              }
            }
          }

          .mark-result {
            padding-top: 12px;
            width: 180px;
            background: #fafafa;
            border-left: 1px solid #d9d9db;

            .result-top {
              display: flex;
              justify-content: space-between;
              align-items: center;
              padding: 0 16px;
              height: 22px;

              span {
                .font14b();

                color: rgba(25, 25, 48, 0.75);
              }

              .result-btn {
                font-size: 16px;
                color: rgba(25, 25, 48, 0.2);

                i {
                  margin-left: 15px;
                  cursor: pointer;
                }
              }
            }

            .result-list {
              overflow-y: scroll;
              padding: 0 16px;
              padding-top: 20px;
              width: 180px;
              flex-shrink: 1;
              height: calc(100% - 22px);

              .result-li {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 4px 8px;
                margin-bottom: 12px;
                height: 30px;
                white-space: nowrap;
                color: rgba(16, 16, 20, 0.95);
                background: rgba(16, 16, 20, 0.08);
                border: 1px solid rgba(0, 0, 0, 0.05);
                border-radius: 6px;
                box-sizing: border-box;
                line-height: 22px;

                .result-li-left {
                  display: flex;
                  align-items: center;
                  width: 80%;

                  .tag-text {
                    display: inline-block;
                    margin-left: 8px;
                    width: 100%;
                    .elliptical-1();
                  }
                }

                .result-li-right {
                  width: 10%;
                  .elliptical-1();

                  cursor: pointer;
                }

                svg path {
                  fill: red;
                }

                .type-img {
                  fill: red;
                  filter: hue-rotate(90deg);

                  svg path {
                    fill: red;
                  }
                  // color: red;
                  // background-color: #fff;
                }
              }
            }
          }
        }
      }
    }
  }
</style>
